home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / pc / technical documentation / develop / additional articles / developing symbiotic apps / symbiotic samples / symbiotic client source / javelin.cw_mw9 / symbio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-05-01  |  39.1 KB  |  1,200 lines

  1. // This file was created from MWTESample.c, a source file from the
  2. //  "MW TESample" folder, which can be found within the "MacOS Examples"
  3. //  folder distributed with Metrowerks CodeWarrior 8.
  4. //
  5. //  When compiling/linking this program you must use the library file
  6. //  DataInit.lib from in the "MW TESample" folder.
  7. //
  8. //  The purpose of this example program is to illustrate the basics
  9. //  programming steps needed to create a client application that
  10. //  utilizes the PPCBrowser and Apple events to communicate with a
  11. //  server application.
  12. //
  13. //  It is NOT the purpose of this example program to illustrate the
  14. //  correct way to display text in a window or refresh a window that
  15. //  is no longer obscured by another.
  16. //
  17. //  This program was quickly put together, and it shows.
  18. //
  19. //  Scott Mulligan  -  Apple Computer, Inc.  -  April, 1996
  20. //
  21. #include <types.h>
  22. #include <quickdraw.h>
  23. #include <fonts.h>
  24. #include <events.h>
  25. #include <controls.h>
  26. #include <windows.h>
  27. #include <menus.h>
  28. #include <textedit.h>
  29. #include <dialogs.h>
  30. #include <desk.h>
  31. #include <scrap.h>
  32. #include <toolutils.h>
  33. //MW must add LowMem for certain functions (see notes).
  34. #include <LowMem.h>
  35. #include <memory.h>
  36. #include <segload.h>
  37. #include <files.h>
  38. #include <osutils.h>
  39. #include <osevents.h>
  40. #include <diskinit.h>
  41. #include <packages.h>
  42. #include <traps.h>
  43. //MW added assembler header (to allow inline assembly)
  44. #include <Assembler.h>
  45.  
  46. #include <PPCToolbox.h>
  47. #include <EPPC.h>
  48. #include <AppleEvents.h>
  49.  
  50. #include "Symbiont.h"
  51. #include "version.h"
  52. #include "Javelin.h"
  53. #include "JavelinEvents.h"
  54.  
  55. typedef struct {
  56.     WindowRecord    docWindow;
  57.     TEHandle        docTE;
  58.     TargetID        TheTarget;
  59.     ProcPtr            docClik;
  60. } DocumentRecord, *DocumentPeek;
  61.  
  62.  
  63. /* The "g" prefix is used to emphasize that a variable is global. */
  64.  
  65. /* GMac is used to hold the result of a SysEnvirons call. This makes
  66.    it convenient for any routine to check the environment. It is
  67.    global information, anyway. */
  68. SysEnvRec    gMac;                /* set up by Initialize */
  69.  
  70. /* GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
  71.    trap is available. If it is false, we know that we must call GetNextEvent. */
  72. Boolean        gHasWaitNextEvent;    /* set up by Initialize */
  73.  
  74. /* GInBackground is maintained by our OSEvent handling routines. Any part of
  75.    the program can check it to find out if it is currently in the background. */
  76. Boolean        gInBackground;        /* maintained by Initialize and DoEvent */
  77.  
  78. /* GNumDocuments is used to keep track of how many open documents there are
  79.    at any time. It is maintained by the routines that open and close documents. */
  80. short        gNumDocuments;        /* maintained by Initialize, DoNew, and DoCloseWindow */
  81.  
  82. // Symbiont globals
  83. long time_of_last_recv=0;
  84. long time_of_last_send=0;
  85. long time_of_last_check=0;
  86. int missed_heartbeats=0;
  87. Boolean TimeToQuit=false;
  88. TargetID TheTarget;
  89. char *ServerOSVer="0.0       ";
  90.  
  91.  
  92. // Standard routines stolen from MWTESample.c    
  93.     void AlertUser( short error );
  94.     void EventLoop( void );
  95.     void DoEvent( EventRecord *event );
  96.     void AdjustCursor( Point mouse, RgnHandle region );
  97.     void GetGlobalMouse( Point *mouse );
  98.     void GetLocalUpdateRgn( WindowPtr window, RgnHandle localRgn );
  99.     void DoUpdate( WindowPtr window );
  100.     void DoActivate( WindowPtr window, Boolean becomingActive );
  101.     unsigned long GetSleep( void );
  102.     void DoIdle( void );
  103.     void DrawWindow( WindowPtr window );
  104.     void AdjustMenus( void );
  105.     void DoMenuCommand( long menuResult );
  106.     void New_Window(void);
  107.     Boolean DoCloseWindow( WindowPtr window );
  108.     void Terminate( void );
  109.     void Initialize( void );
  110.     void BigBadError( short error );
  111.     void GetTERect( WindowPtr window, Rect *teRect );
  112.     void AdjustViewRect( TEHandle docTE );
  113.     void AdjustHV( Boolean isVert, ControlHandle control, TEHandle docTE,
  114.                     Boolean canRedraw );
  115.     pascal void PascalClikLoop(void);
  116.     pascal ProcPtr GetOldClikLoop(void);
  117.     Boolean IsAppWindow( WindowPtr window );
  118.     Boolean IsDAWindow( WindowPtr window );
  119.     Boolean TrapAvailable( short tNumber, TrapType tType );
  120.  
  121. // Generic symbiotic functions
  122. extern pascal OSErr HandleHeartBeat(AppleEvent *msg,AppleEvent *reply,long refcon);
  123. extern pascal OSErr HandleQuit(AppleEvent *msg,AppleEvent *reply,long refcon);
  124. extern pascal Boolean snd_version(void);
  125. extern void chk_heartbeat(void);
  126. extern void snd_heartbeat(void);
  127. extern void snd_quit(void);
  128. extern void snd_null(void);
  129.  
  130. // External functions specific to this symbiont
  131. void new_connection(void);
  132. void InstallAppleEventHandlers(void);
  133.  
  134.  
  135. /* Define HiWrd and LoWrd macros for efficiency. */
  136. #define HiWrd(aLong)    (((aLong) >> 16) & 0xFFFF)
  137. #define LoWrd(aLong)    ((aLong) & 0xFFFF)
  138.  
  139. /* Define TopLeft and BotRight macros for convenience. Notice the implicit
  140.    dependency on the ordering of fields within a Rect */
  141. #define TopLeft(aRect)    (* (Point *) &(aRect).top)
  142. #define BotRight(aRect)    (* (Point *) &(aRect).bottom)
  143.  
  144.  
  145. /* This routine is part of the MPW runtime library. This external
  146.    reference to it is done so that we can unload its segment, %A5Init. */
  147.  
  148. //MW added void argument
  149. extern void _DataInit(void);
  150.  
  151. /* A reference to our assembly language routine that gets attached to the clikLoop
  152. field of our TE record. */
  153.  
  154.  
  155. //MW added void argument.
  156. pascal void AsmClikLoop(void);
  157.  
  158. //MW added the MPW assembly function in a C function.
  159. asm pascal void AsmClikLoop(void)
  160. {
  161.     MOVEM.L        D1-D2/A1,-(SP)        // D0 and A0 need not be saved
  162.     CLR.L        -(SP)                // make space for procedure pointer
  163.     JSR            GetOldClikLoop        // get the old clikLoop
  164.     MOVEA.L        (SP)+,A0            // into A0
  165.     MOVEM.L        (SP)+,D1-D2/A1        // restore the world as it was
  166.     
  167.     JSR            (A0)                // and execute old clikLoop
  168.  
  169.     MOVEM.L        D1-D2/A1,-(SP)        // D0 and A0 need not be saved
  170.     JSR            PascalClikLoop        // do our clikLoop
  171.     MOVEM.L        (SP)+,D1-D2/A1        // restore the world as it was
  172.     MOVEQ        #1,D0                // clear the zero flag so TextEdit keeps going
  173.     RTS
  174. }
  175.  
  176. #pragma segment Main
  177. //MW added void return-type and argument
  178. void main(void)
  179. {
  180.     UnloadSeg((Ptr) _DataInit);        /* note that _DataInit must not be in Main! */
  181.     
  182.     /* 1.01 - call to ForceEnvirons removed */
  183.     
  184.     /*    If you have stack requirements that differ from the default,
  185.         then you could use SetApplLimit to increase StackSpace at 
  186.         this point, before calling MaxApplZone. */
  187.     MaxApplZone();                    /* expand the heap so code segments load at the top */
  188.  
  189.     Initialize();                    /* initialize the program */
  190.     UnloadSeg((Ptr) Initialize);    /* note that Initialize must not be in Main! */
  191.  
  192.     (void)InstallAppleEventHandlers();    // Install the AppleEvent handler functions
  193.  
  194.     (void)new_connection();                // Bring up PPC Browser for initial connection
  195.     
  196.     EventLoop();                    /* call the main event loop */
  197. }
  198.  
  199.  
  200. /* Get events forever, and handle them by calling DoEvent.
  201.    Also call AdjustCursor each time through the loop. */
  202.  
  203. #pragma segment Main
  204. //MW added void argument
  205. void EventLoop(void)
  206. {
  207.     RgnHandle    cursorRgn;
  208.     Boolean        gotEvent;
  209.     EventRecord    event;
  210.     Point        mouse;
  211.  
  212.     cursorRgn = NewRgn();            /* we’ll pass WNE an empty region the 1st time thru */
  213.     do {
  214.         if ( gHasWaitNextEvent ) {  /* use WNE if it is available */
  215.             GetGlobalMouse(&mouse);
  216.             AdjustCursor(mouse, cursorRgn);
  217.             gotEvent = WaitNextEvent(everyEvent, &event, GetSleep(), cursorRgn);
  218.         }
  219.         else {
  220.             SystemTask();
  221.             gotEvent = GetNextEvent(everyEvent, &event);
  222.         }
  223.         if ( gotEvent ) {
  224.             /* make sure we have the right cursor before handling the event */
  225.             AdjustCursor(event.where, cursorRgn);
  226.             DoEvent(&event);
  227.         }
  228.         else
  229.             DoIdle();                /* perform idle tasks when it’s not our event */
  230.         /*    If you are using modeless dialogs that have editText items,
  231.             you will want to call IsDialogEvent to give the caret a chance
  232.             to blink, even if WNE/GNE returned FALSE. However, check FrontWindow
  233.             for a non-NIL value before calling IsDialogEvent. */
  234.     } while ( true );    /* loop forever; we quit via ExitToShell */
  235. } /*EventLoop*/
  236.  
  237.  
  238. /* Do the right thing for an event. Determine what kind of event it is, and call
  239.  the appropriate routines. */
  240.  
  241. #pragma segment Main
  242. //MW Changed prototype style to new C (old C to new C)
  243. void DoEvent(EventRecord *event)    
  244. {
  245.     short        part, err;
  246.     WindowPtr    window;
  247.     char        key;
  248.     Point        aPoint;
  249.  
  250.     switch ( event->what ) {
  251.         case kHighLevelEvent:
  252.             AEProcessAppleEvent(event);
  253.             break;
  254.         case nullEvent:
  255.             /* we idle for null/mouse moved events ands for events which aren’t
  256.                 ours (see EventLoop) */
  257.             DoIdle();
  258.             break;
  259.         case mouseDown:
  260.             part = FindWindow(event->where, &window);
  261.             switch ( part ) {
  262.                 case inMenuBar:             /* process a mouse menu command (if any) */
  263.                     AdjustMenus();    /* bring ’em up-to-date */
  264.                     DoMenuCommand(MenuSelect(event->where));
  265.                     break;
  266.                 case inSysWindow:           /* let the system handle the mouseDown */
  267.                     SystemClick(event, window);
  268.                     break;
  269.                 case inContent:
  270.                     if ( window != FrontWindow() ) {
  271.                         SelectWindow(window);
  272.                         /*DoEvent(event);*/    /* use this line for "do first click" */
  273.                     } 
  274.                     break;
  275.                 case inDrag:                /* pass screenBits.bounds to get all gDevices */
  276.                     DragWindow(window, event->where, &qd.screenBits.bounds);
  277.                     break;
  278.                 case inGoAway:
  279.                     if ( TrackGoAway(window, event->where) )
  280.                         DoCloseWindow(window); /* we don’t care if the user cancelled */
  281.                     break;
  282.             }
  283.             break;
  284.         case keyDown:
  285.         case autoKey:                       /* check for menukey equivalents */
  286.             key = event->message & charCodeMask;
  287.             if ( event->modifiers & cmdKey ) {    /* Command key down */
  288.                 if ( event->what == keyDown ) {
  289.                     AdjustMenus();            /* enable/disable/check menu items properly */
  290.                     DoMenuCommand(MenuKey(key));
  291.                 }
  292.             }
  293.             break;
  294.         case activateEvt:
  295.             DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
  296.             break;
  297.         case updateEvt:
  298. //            DoUpdate((WindowPtr) event->message);
  299.             break;
  300.         /*    1.01 - It is not a bad idea to at least call DIBadMount in response
  301.             to a diskEvt, so that the user can format a floppy. */
  302.         case diskEvt:
  303.             if ( HiWord(event->message) != noErr ) {
  304.                 SetPt(&aPoint, kDILeft, kDITop);
  305.                 err = DIBadMount(aPoint, event->message);
  306.             }
  307.             break;
  308.         case kOSEvent:
  309.         /*    1.02 - must BitAND with 0x0FF to get only low byte */
  310.             switch ((event->message >> 24) & 0x0FF) {        /* high byte of message */
  311.                 case kMouseMovedMessage:
  312.                     DoIdle();                    /* mouse-moved is also an idle event */
  313.                     break;
  314.                 case kSuspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  315.                     gInBackground = (event->message & kResumeMask) == 0;
  316.                     DoActivate(FrontWindow(), !gInBackground);
  317.                     break;
  318.             }
  319.             break;
  320.     }
  321. } /*DoEvent*/
  322.  
  323.  
  324. /*    Change the cursor's shape, depending on its position. This also calculates the region
  325.     where the current cursor resides (for WaitNextEvent). When the mouse moves outside of
  326.     this region, an event is generated. If there is more to the event than just
  327.     “the mouse moved”, we get called before the event is processed to make sure
  328.     the cursor is the right one. In any (ahem) event, this is called again before we
  329.     fall back into WNE. */
  330.  
  331. #pragma segment Main
  332. //MW Changed prototype style to new C
  333. void AdjustCursor(Point    mouse,RgnHandle    region)
  334. {
  335.     WindowPtr    window;
  336.     RgnHandle    arrowRgn;
  337.     RgnHandle    iBeamRgn;
  338.     Rect        iBeamRect;
  339.  
  340.     window = FrontWindow();    /* we only adjust the cursor when we are in front */
  341.     if ( (! gInBackground) && (! IsDAWindow(window)) ) {
  342.         /* calculate regions for different cursor shapes */
  343.         arrowRgn = NewRgn();
  344.         iBeamRgn = NewRgn();
  345.  
  346.         /* start arrowRgn wide open */
  347.         SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
  348.  
  349.         /* calculate iBeamRgn */
  350.         if ( IsAppWindow(window) ) {
  351.             iBeamRect = (*((DocumentPeek) window)->docTE)->viewRect;
  352.             SetPort(window);    /* make a global version of the viewRect */
  353.             LocalToGlobal(&TopLeft(iBeamRect));
  354.             LocalToGlobal(&BotRight(iBeamRect));
  355.             RectRgn(iBeamRgn, &iBeamRect);
  356.             /* we temporarily change the port’s origin to “globalfy” the visRgn */
  357.             SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);
  358.             SectRgn(iBeamRgn, window->visRgn, iBeamRgn);
  359.             SetOrigin(0, 0);
  360.         }
  361.  
  362.         /* subtract other regions from arrowRgn */
  363.         DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
  364.  
  365.         /* change the cursor and the region parameter */
  366.         if ( PtInRgn(mouse, iBeamRgn) ) {
  367.             SetCursor(*GetCursor(iBeamCursor));
  368.             CopyRgn(iBeamRgn, region);
  369.         } else {
  370.             SetCursor(&qd.arrow);
  371.             CopyRgn(arrowRgn, region);
  372.         }
  373.  
  374.         DisposeRgn(arrowRgn);
  375.         DisposeRgn(iBeamRgn);
  376.     }
  377. } /*AdjustCursor*/
  378.  
  379.  
  380. /*    Get the global coordinates of the mouse. When you call OSEventAvail
  381.     it will return either a pending event or a null event. In either case,
  382.     the where field of the event record will contain the current position
  383.     of the mouse in global coordinates and the modifiers field will reflect
  384.     the current state of the modifiers. Another way to get the global
  385.     coordinates is to call GetMouse and LocalToGlobal, but that requires
  386.     being sure that thePort is set to a valid port. */
  387.  
  388. #pragma segment Main
  389. //MW Changed prototype style to new C (old C to new C)
  390. void GetGlobalMouse(Point *mouse)
  391. {
  392.     EventRecord    event;
  393.     
  394.     OSEventAvail(kNoEvents, &event);    /* we aren't interested in any events */
  395.     *mouse = event.where;                /* just the mouse position */
  396. } /*GetGlobalMouse*/
  397.  
  398.  
  399. /* Returns the update region in local coordinates */
  400. #pragma segment Main
  401. //MW Changed prototype style to new C (old C to new C)
  402. void GetLocalUpdateRgn(WindowPtr window,RgnHandle localRgn)
  403. {
  404.     CopyRgn(((WindowPeek) window)->updateRgn, localRgn);    /* save old update region */
  405.     OffsetRgn(localRgn, window->portBits.bounds.left, window->portBits.bounds.top);
  406. } /* GetLocalUpdateRgn */
  407.  
  408.  
  409. /*    This is called when an update event is received for a window.
  410.     It calls DrawWindow to draw the contents of an application window.
  411.     As an efficiency measure that does not have to be followed, it
  412.     calls the drawing routine only if the visRgn is non-empty. This
  413.     will handle situations where calculations for drawing or drawing
  414.     itself is very time-consuming. */
  415.  
  416. #pragma segment Main
  417. //MW Changed prototype style to new C (old C to new C)
  418. void DoUpdate(WindowPtr    window)
  419. {
  420.     if ( IsAppWindow(window) ) {
  421.         BeginUpdate(window);                /* this sets up the visRgn */
  422.         if ( ! EmptyRgn(window->visRgn) )    /* draw if updating needs to be done */
  423.             DrawWindow(window);
  424.         EndUpdate(window);
  425.     }
  426. } /*DoUpdate*/
  427.  
  428.  
  429. /*    This is called when a window is activated or deactivated.
  430.     It calls TextEdit to deal with the selection. */
  431.  
  432. #pragma segment Main
  433. //MW Changed prototype style to new C (old C to new C)
  434. void DoActivate(WindowPtr window, Boolean becomingActive)
  435. {
  436.     RgnHandle    tempRgn, clipRgn;
  437.     DocumentPeek doc;
  438.     
  439.     if ( IsAppWindow(window) ) {
  440.         doc = (DocumentPeek) window;
  441.         if ( becomingActive ) {
  442.             /*    since we don’t want TEActivate to draw a selection in an area where
  443.                 we’re going to erase and redraw, we’ll clip out the update region
  444.                 before calling it. */
  445.             tempRgn = NewRgn();
  446.             clipRgn = NewRgn();
  447.             GetLocalUpdateRgn(window, tempRgn);            /* get localized update region */
  448.             GetClip(clipRgn);
  449.             DiffRgn(clipRgn, tempRgn, tempRgn);            /* subtract updateRgn from clipRgn */
  450.             SetClip(tempRgn);
  451.             TEActivate(doc->docTE);
  452.             SetClip(clipRgn);                            /* restore the full-blown clipRgn */
  453.             DisposeRgn(tempRgn);
  454.             DisposeRgn(clipRgn);
  455.             
  456.         }
  457.         else {        
  458.             TEDeactivate(doc->docTE);
  459.             /* the controls must be hidden on deactivation: */
  460.         }
  461.     }
  462. } /*DoActivate*/
  463.  
  464.  
  465. /*    Calculate a sleep value for WaitNextEvent. This takes into account the things
  466.     that DoIdle does with idle time. */
  467.  
  468. #pragma segment Main
  469. //MW Added argument type (void)
  470. unsigned long GetSleep(void)
  471. {
  472.     long        sleep;
  473.     WindowPtr    window;
  474.     TEHandle    te;
  475.  
  476. // MW MAXLONG was previously defined in values.h but Apple tells users to now use
  477. // MW the constants defined in the ANSI headers
  478. //    sleep = MAXLONG;                        /* default value for sleep */
  479.     sleep = 2147483647;                        /* default value for sleep */
  480.     if ( !gInBackground ) {
  481.         window = FrontWindow();            /* and the front window is ours... */
  482.         if ( IsAppWindow(window) ) {
  483.             te = ((DocumentPeek) (window))->docTE;    /* and the selection is an insertion point... */
  484.             if ( (*te)->selStart == (*te)->selEnd )
  485.                 sleep = GetCaretTime();        /* blink time for the insertion point */
  486.         }
  487.     }
  488.     return sleep;
  489. } /*GetSleep*/
  490.  
  491.  
  492.  
  493. /* This is called whenever we get a null event et al.
  494.  It takes care of necessary periodic actions. For this program, it calls TEIdle. */
  495.  
  496. #pragma segment Main
  497. //MW Added argument type (void).
  498. void DoIdle(void)
  499. {
  500.     WindowPtr    window;
  501.  
  502.     window = FrontWindow();
  503.     if ( IsAppWindow(window) ) {
  504.         TEIdle(((DocumentPeek) window)->docTE);
  505.         if (TickCount() - time_of_last_send >= HEARTBEAT_SEND) snd_heartbeat();
  506.         if (TickCount() - time_of_last_check >= HEARTBEAT_CHECK) chk_heartbeat();
  507.         if (TimeToQuit == true) Terminate();
  508.     }
  509. } /*DoIdle*/
  510.  
  511.  
  512. /* Draw the contents of an application window. */
  513.  
  514. #pragma segment Main
  515. //MW Changed prototype style to new C (old C to new C)
  516. void DrawWindow(WindowPtr    window)
  517. {
  518.     SetPort(window);
  519.     EraseRect(&window->portRect);
  520.     DrawControls(window);
  521.     DrawGrowIcon(window);
  522.     TEUpdate(&window->portRect, ((DocumentPeek) window)->docTE);
  523. } /*DrawWindow*/
  524.  
  525.  
  526. /*    Enable and disable menus based on the current state.
  527.     The user can only select enabled menu items. We set up all the menu items
  528.     before calling MenuSelect or MenuKey, since these are the only times that
  529.     a menu item can be selected. Note that MenuSelect is also the only time
  530.     the user will see menu items. This approach to deciding what enable/
  531.     disable state a menu item has the advantage of concentrating all
  532.     the decision-making in one routine, as opposed to being spread throughout
  533.     the application. Other application designs may take a different approach
  534.     that may or may not be as valid. */
  535.  
  536. #pragma segment Main
  537. //MW Added argument type (void).
  538. void AdjustMenus(void)
  539. {
  540.     WindowPtr    window;
  541.     MenuHandle    menu;
  542.     long        offset;
  543.     Boolean        undo;
  544.     Boolean        cutCopyClear;
  545.     Boolean        paste;
  546.     TEHandle    te;
  547.  
  548.     window = FrontWindow();
  549.  
  550.     menu = GetMHandle(mFile);
  551.     if ( gNumDocuments < kMaxOpenDocuments )
  552.         EnableItem(menu, iNew);        /* New is enabled when we can open more documents */
  553.     else
  554.         DisableItem(menu, iNew);
  555.  
  556.     menu = GetMHandle(mEdit);
  557.     undo = false;
  558.     cutCopyClear = false;
  559.     paste = false;
  560.     if ( IsDAWindow(window) ) {
  561.         undo = true;                /* all editing is enabled for DA windows */
  562.         cutCopyClear = true;
  563.         paste = true;
  564.     } else if ( IsAppWindow(window) ) {
  565.         te = ((DocumentPeek) window)->docTE;
  566.         if ( (*te)->selStart < (*te)->selEnd )
  567.             cutCopyClear = false;
  568.             /* Cut, Copy, and Clear is enabled for app. windows with selections */
  569.         if ( GetScrap(nil, 'TEXT', &offset)  > 0)
  570.             paste = false;            /* if there’s any text in the clipboard, paste is enabled */
  571.     }
  572.     if ( undo )
  573.         EnableItem(menu, iUndo);
  574.     else
  575.         DisableItem(menu, iUndo);
  576.     if ( cutCopyClear ) {
  577.         EnableItem(menu, iCut);
  578.         EnableItem(menu, iCopy);
  579.         EnableItem(menu, iClear);
  580.     } else {
  581.         DisableItem(menu, iCut);
  582.         DisableItem(menu, iCopy);
  583.         DisableItem(menu, iClear);
  584.     }
  585.     if ( paste )
  586.         EnableItem(menu, iPaste);
  587.     else
  588.         DisableItem(menu, iPaste);
  589. } /*AdjustMenus*/
  590.  
  591.  
  592. /*    This is called when an item is chosen from the menu bar (after calling
  593.     MenuSelect or MenuKey). It does the right thing for each command. */
  594.  
  595. #pragma segment Main
  596. //MW Changed prototype style to new C (old C to new C)
  597. void DoMenuCommand(long menuResult)
  598. {
  599.     short        menuID, menuItem;
  600.     short        itemHit, daRefNum;
  601.     Str255        daName;
  602.     WindowPtr    window;
  603.  
  604.     window = FrontWindow();
  605.     menuID = HiWord(menuResult);    /* use macros for efficiency to... */
  606.     menuItem = LoWord(menuResult);    /* get menu item number and menu number */
  607.     switch ( menuID ) {
  608.         case mApple:
  609.             switch ( menuItem ) {
  610.                 case iAbout:        /* bring up alert for About */
  611.                     itemHit = Alert(rAboutAlert, nil);
  612.                     break;
  613.                 default:            /* all non-About items in this menu are DAs et al */
  614.                     /* type Str255 is an array in MPW 3 */
  615.                     GetItem(GetMHandle(mApple), menuItem, daName);
  616.                     daRefNum = OpenDeskAcc(daName);
  617.                     break;
  618.             }
  619.             break;
  620.         case mFile:
  621.             switch ( menuItem ) {
  622.                 case iNew:
  623.                     new_connection();
  624.                     break;
  625.                 case iQuit:
  626.                     Terminate();
  627.                     break;
  628.             }
  629.             break;
  630.     }
  631.     HiliteMenu(0);                    /* unhighlight what MenuSelect (or MenuKey) hilited */
  632. } /*DoMenuCommand*/
  633.  
  634.  
  635. // Create a new document and window.
  636. //
  637. #pragma segment Main
  638. void New_Window(void)
  639. {
  640.     Boolean        good;
  641.     Ptr            storage;
  642.     WindowPtr    window;
  643.     Rect        destRect, viewRect;
  644.     DocumentPeek doc;
  645.     
  646.     extern Str255 hostname;
  647.     
  648.     
  649.     storage = NewPtr(sizeof(DocumentRecord));
  650.     if ( storage != nil ) {
  651.         window = GetNewWindow(rJavWindow, storage, (WindowPtr) -1);
  652.         SetWTitle(window,hostname);
  653.         if ( window != nil ) {
  654.             gNumDocuments += 1;            /* this will be decremented when we call DoCloseWindow */
  655.             good = false;
  656.             SetPort(window);
  657.             doc =  (DocumentPeek) window;
  658.             GetTERect(window, &viewRect);
  659.             destRect = viewRect;
  660.             destRect.right = destRect.left + kMaxDocWidth;
  661.             doc->docTE = TENew(&destRect, &viewRect);
  662.             good = doc->docTE != nil;    /* if TENew succeeded, we have a good document */
  663.             if ( good ) {                /* 1.02 - good document? — proceed */
  664. //                AdjustViewRect(doc->docTE);
  665.                 TEAutoView(true, doc->docTE);
  666. // MW Changed to conform to SDK 7 headers
  667. //                doc->docClik = (ProcPtr) (*doc->docTE)->clikLoop;
  668. //                (*doc->docTE)->clikLoop = (ClikLoopUPP) AsmClikLoop;
  669.                 doc->docClik = (ProcPtr) (*doc->docTE)->clickLoop;
  670.                 (*doc->docTE)->clickLoop = (TEClickLoopUPP) AsmClikLoop;
  671.             }
  672.             
  673.             if ( good ) {                /* good? — draw the window */
  674.                 ShowWindow(window);
  675.             } else {
  676.                 DoCloseWindow(window);    /* otherwise regret we ever created it... */
  677.                 AlertUser(eNoWindow);            /* and tell user */
  678.             }
  679.         } else
  680.             DisposPtr(storage);            /* get rid of the storage if it is never used */
  681.     }
  682. } /*New_Window*/
  683.  
  684.  
  685. /* Close a window. This handles desk accessory and application windows. */
  686.  
  687. /*    1.01 - At this point, if there was a document associated with a
  688.     window, you could do any document saving processing if it is 'dirty'.
  689.     DoCloseWindow would return true if the window actually closed, i.e.,
  690.     the user didn’t cancel from a save dialog. This result is handy when
  691.     the user quits an application, but then cancels the save of a document
  692.     associated with a window. */
  693.  
  694. #pragma segment Main
  695. //MW Changed prototype style to new C (old C to new C)
  696. Boolean DoCloseWindow(WindowPtr    window)
  697. {
  698.     TEHandle    te;
  699.  
  700.     if ( IsDAWindow(window) )
  701.         CloseDeskAcc(((WindowPeek) window)->windowKind);
  702.     else if ( IsAppWindow(window) ) {
  703.         te = ((DocumentPeek) window)->docTE;
  704.         if ( te != nil )
  705.             TEDispose(te);            /* dispose the TEHandle if we got far enough to make one */
  706.         /*    1.01 - We used to call DisposeWindow, but that was technically
  707.             incorrect, even though we allocated storage for the window on
  708.             the heap. We should instead call CloseWindow to have the structures
  709.             taken care of and then dispose of the storage ourselves. */
  710.         CloseWindow(window);
  711.         DisposPtr((Ptr) window);
  712.         gNumDocuments -= 1;
  713.     }
  714.     return true;
  715. } /*DoCloseWindow*/
  716.  
  717.  
  718. /**************************************************************************************
  719. *** 1.01 DoCloseBehind(window) was removed ***
  720.  
  721.     1.01 - DoCloseBehind was a good idea for closing windows when quitting
  722.     and not having to worry about updating the windows, but it suffered
  723.     from a fatal flaw. If a desk accessory owned two windows, it would
  724.     close both those windows when CloseDeskAcc was called. When DoCloseBehind
  725.     got around to calling DoCloseWindow for that other window that was already
  726.     closed, things would go very poorly. Another option would be to have a
  727.     procedure, GetRearWindow, that would go through the window list and return
  728.     the last window. Instead, we decided to present the standard approach
  729.     of getting and closing FrontWindow until FrontWindow returns NIL. This
  730.     has a potential benefit in that the window whose document needs to be saved
  731.     may be visible since it is the front window, therefore decreasing the
  732.     chance of user confusion. For aesthetic reasons, the windows in the
  733.     application should be checked for updates periodically and have the
  734.     updates serviced.
  735. **************************************************************************************/
  736.  
  737.  
  738. /* Clean up the application and exit. We close all of the windows so that
  739.  they can update their documents, if any. */
  740.  
  741. /*    1.01 - If we find out that a cancel has occurred, we won't exit to the
  742.     shell, but will return instead. */
  743.  
  744. #pragma segment Main
  745. //MW Added argument type (void).
  746. void Terminate(void)
  747. {
  748.     WindowPtr    aWindow;
  749.     Boolean        closed;
  750.     
  751.     closed = true;
  752.     do {
  753.         aWindow = FrontWindow();                /* get the current front window */
  754.         if (aWindow != nil)
  755.             closed = DoCloseWindow(aWindow);    /* close this window */    
  756.     }
  757.     while (closed && (aWindow != nil));
  758.     if (closed) {
  759.         snd_quit();
  760.         ExitToShell();    
  761.     }                        /* exit if no cancellation */
  762. } /*Terminate*/
  763.  
  764.  
  765. /*    Set up the whole world, including global variables, Toolbox managers,
  766.     menus, and a single blank document. */
  767.  
  768. /*    1.01 - The code that used to be part of ForceEnvirons has been moved into
  769.     this module. If an error is detected, instead of merely doing an ExitToShell,
  770.     which leaves the user without much to go on, we call AlertUser, which puts
  771.     up a simple alert that just says an error occurred and then calls ExitToShell.
  772.     Since there is no other cleanup needed at this point if an error is detected,
  773.     this form of error- handling is acceptable. If more sophisticated error recovery
  774.     is needed, an exception mechanism, such as is provided by Signals, can be used. */
  775.  
  776. #pragma segment Initialize
  777. //MW Added argument type (void).
  778. void Initialize(void)
  779. {
  780.     Handle    menuBar;
  781.     long    total, contig;
  782.     EventRecord event;
  783.     short    count;
  784.  
  785.     gInBackground = false;
  786.  
  787.     InitGraf((Ptr) &qd.thePort);
  788.     InitFonts();
  789.     InitWindows();
  790.     InitMenus();
  791.     TEInit();
  792.     InitDialogs(nil);
  793.     InitCursor();
  794.  
  795.     /*    Call MPPOpen and ATPLoad at this point to initialize AppleTalk,
  796.          if you are using it. */
  797.     /*    NOTE -- It is no longer necessary, and actually unhealthy, to check
  798.         PortBUse and SPConfig before opening AppleTalk. The drivers are capable
  799.         of checking for port availability themselves. */
  800.     
  801.     /*    This next bit of code is necessary to allow the default button of our
  802.         alert be outlined.
  803.         1.02 - Changed to call EventAvail so that we don't lose some important
  804.         events. */
  805.      
  806.     for (count = 1; count <= 3; count++)
  807.         EventAvail(everyEvent, &event);
  808.     
  809.     /*    Ignore the error returned from SysEnvirons; even if an error occurred,
  810.         the SysEnvirons glue will fill in the SysEnvRec. You can save a redundant
  811.         call to SysEnvirons by calling it after initializing AppleTalk. */
  812.      
  813.     SysEnvirons(kSysEnvironsVersion, &gMac);
  814.     
  815.     /* Make sure that the machine has at least 128K ROMs. If it doesn't, exit. */
  816.     
  817.     if (gMac.machineType < 0) BigBadError(eWrongMachine);
  818.     
  819.     /*    1.02 - Move TrapAvailable call to after SysEnvirons so that we can tell
  820.         in TrapAvailable if a tool trap value is out of range. */
  821.         
  822.     gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
  823.  
  824.     /*    1.01 - We used to make a check for memory at this point by examining ApplLimit,
  825.         ApplicZone, and StackSpace and comparing that to the minimum size we told
  826.         MultiFinder we needed. This did not work well because it assumed too much about
  827.         the relationship between what we asked MultiFinder for and what we would actually
  828.         get back, as well as how to measure it. Instead, we will use an alternate
  829.         method comprised of two steps. */
  830.      
  831.     /*    It is better to first check the size of the application heap against a value
  832.         that you have determined is the smallest heap the application can reasonably
  833.         work in. This number should be derived by examining the size of the heap that
  834.         is actually provided by MultiFinder when the minimum size requested is used.
  835.         The derivation of the minimum size requested from MultiFinder is described
  836.         in Sample.h. The check should be made because the preferred size can end up
  837.         being set smaller than the minimum size by the user. This extra check acts to
  838.         insure that your application is starting from a solid memory foundation. */
  839.      
  840.     if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap) BigBadError(eSmallSize);
  841.     
  842.     /*    Next, make sure that enough memory is free for your application to run. It
  843.         is possible for a situation to arise where the heap may have been of required
  844.         size, but a large scrap was loaded which left too little memory. To check for
  845.         this, call PurgeSpace and compare the result with a value that you have determined
  846.         is the minimum amount of free memory your application needs at initialization.
  847.         This number can be derived several different ways. One way that is fairly
  848.         straightforward is to run the application in the minimum size configuration
  849.         as described previously. Call PurgeSpace at initialization and examine the value
  850.         returned. However, you should make sure that this result is not being modified
  851.         by the scrap's presence. You can do that by calling ZeroScrap before calling
  852.         PurgeSpace. Make sure to remove that call before shipping, though. */
  853.     
  854.     /* ZeroScrap(); */
  855.  
  856.     PurgeSpace(&total, &contig);
  857.     if (total < kMinSpace)
  858.         if (UnloadScrap() != noErr)
  859.             BigBadError(eNoMemory);
  860.         else {
  861.             PurgeSpace(&total, &contig);
  862.             if (total < kMinSpace)
  863.                 BigBadError(eNoMemory);
  864.         }
  865.  
  866.     /*    The extra benefit to waiting until after the Toolbox Managers have been initialized
  867.         to check memory is that we can now give the user an alert to tell him/her what
  868.         happened. Although it is possible that the memory situation could be worsened by
  869.         displaying an alert, MultiFinder would gracefully exit the application with
  870.         an informative alert if memory became critical. Here we are acting more
  871.         in a preventative manner to avoid future disaster from low-memory problems. */
  872.  
  873.     menuBar = GetNewMBar(rMenuBar);            /* read menus into menu bar */
  874.     if ( menuBar == nil )
  875.                 BigBadError(eNoMemory);
  876.     SetMenuBar(menuBar);                    /* install menus */
  877.     DisposHandle(menuBar);
  878.     AddResMenu(GetMHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  879.     DrawMenuBar();
  880.  
  881.     gNumDocuments = 0;
  882.  
  883.     /* do other initialization here */
  884.  
  885. } /*Initialize*/
  886.  
  887.  
  888. /* Used whenever a, like, fully fatal error happens */
  889. #pragma segment Initialize
  890. //MW Changed prototype style to new C (old C to new C)
  891. void BigBadError(short error)
  892. {
  893.     AlertUser(error);
  894.     ExitToShell();
  895. }
  896.  
  897.  
  898. /* Return a rectangle that is inset from the portRect by the size of
  899.     the scrollbars and a little extra margin. */
  900.  
  901. #pragma segment Main
  902. //MW Changed prototype style to new C (old C to new C)
  903. void GetTERect(WindowPtr    window,Rect        *teRect)
  904. {
  905.     *teRect = window->portRect;
  906.     InsetRect(teRect, 0, 5);    /* adjust for margin */
  907. } /*GetTERect*/
  908.  
  909.  
  910. /* Update the TERec's view rect so that it is the greatest multiple of
  911.     the lineHeight that still fits in the old viewRect. */
  912.  
  913. #pragma segment Main
  914. //MW Changed prototype style to new C (old C to new C)
  915. void AdjustViewRect(TEHandle    docTE)
  916. {
  917.     TEPtr        te;
  918.     
  919.     te = *docTE;
  920.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  921.                             * te->lineHeight) + te->viewRect.top;
  922. } /*AdjustViewRect*/
  923.  
  924.  
  925. #pragma segment Main
  926. Boolean        needsResize;
  927. //MW Added argument type (void).
  928. pascal  void PascalClikLoop(void)
  929. {
  930.     WindowPtr    window;
  931.     RgnHandle    region;
  932.     
  933.     window = FrontWindow();
  934.     region = NewRgn();
  935.     GetClip(region);                    /* save clip */
  936.     ClipRect(&window->portRect);
  937.     SetClip(region);                    /* restore clip */
  938.     DisposeRgn(region);
  939. } /* Pascal/C ClikLoop */
  940.  
  941.  
  942. /* Gets called from our assembly language routine, AsmClikLoop, which is in
  943.     turn called by the TEClick toolbox routine. It returns the address of the
  944.     default clikLoop routine that was put into the TERec by TEAutoView to
  945.     AsmClikLoop so that it can call it. */
  946.  
  947. #pragma segment Main
  948. //MW Added argument type.
  949. pascal ProcPtr GetOldClikLoop(void)
  950. {
  951.     return ((DocumentPeek)FrontWindow())->docClik;
  952. } /* GetOldClikLoop */
  953.  
  954.  
  955. /*    Check to see if a window belongs to the application. If the window pointer
  956.     passed was NIL, then it could not be an application window. WindowKinds
  957.     that are negative belong to the system and windowKinds less than userKind
  958.     are reserved by Apple except for windowKinds equal to dialogKind, which
  959.     mean it is a dialog.
  960.     1.02 - In order to reduce the chance of accidentally treating some window
  961.     as an AppWindow that shouldn't be, we'll only return true if the windowkind
  962.     is userKind. If you add different kinds of windows to Sample you'll need
  963.     to change how this all works. */
  964.  
  965. #pragma segment Main
  966. //MW Changed prototype style to new C (old C to new C)
  967. Boolean IsAppWindow(WindowPtr window)
  968. {
  969.     short        windowKind;
  970.  
  971.     if ( window == nil )
  972.         return false;
  973.     else {    /* application windows have windowKinds = userKind (8) */
  974.         windowKind = ((WindowPeek) window)->windowKind;
  975.         return (windowKind == userKind);
  976.     }
  977. } /*IsAppWindow*/
  978.  
  979.  
  980. /* Check to see if a window belongs to a desk accessory. */
  981.  
  982. #pragma segment Main
  983. //MW Changed prototype style to new C (old C to new C)
  984. Boolean IsDAWindow(WindowPtr window)
  985. {
  986.     if ( window == nil )
  987.         return false;
  988.     else    /* DA windows have negative windowKinds */
  989.         return ((WindowPeek) window)->windowKind < 0;
  990. } /*IsDAWindow*/
  991.  
  992.  
  993. /*    Check to see if a given trap is implemented. This is only used by the
  994.     Initialize routine in this program, so we put it in the Initialize segment.
  995.     The recommended approach to see if a trap is implemented is to see if
  996.     the address of the trap routine is the same as the address of the
  997.     Unimplemented trap. */
  998. /*    1.02 - Needs to be called after call to SysEnvirons so that it can check
  999.     if a ToolTrap is out of range of a pre-MacII ROM. */
  1000.  
  1001. #pragma segment Initialize
  1002. //MW Changed prototype style to new C (old C to new C)
  1003. Boolean TrapAvailable(short tNumber,TrapType tType)
  1004. {
  1005.     if ( ( tType == (unsigned char) ToolTrap ) &&
  1006.         ( gMac.machineType > envMachUnknown ) &&
  1007.         ( gMac.machineType < envMacII ) ) {        /* it's a 512KE, Plus, or SE */
  1008.         tNumber = tNumber & 0x03FF;
  1009.         if ( tNumber > 0x01FF )                    /* which means the tool traps */
  1010.             tNumber = _Unimplemented;            /* only go to 0x01FF */
  1011.     }
  1012.     return NGetTrapAddress(tNumber, tType) != GetToolTrapAddress(_Unimplemented);
  1013. // MW    return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
  1014. } /*TrapAvailable*/
  1015.  
  1016.  
  1017. /*    Display an alert that tells the user an error occurred, then exit the program.
  1018.     This routine is used as an ultimate bail-out for serious errors that prohibit
  1019.     the continuation of the application. Errors that do not require the termination
  1020.     of the application should be handled in a different manner. Error checking and
  1021.     reporting has a place even in the simplest application. The error number is used
  1022.     to index an 'STR#' resource so that a relevant message can be displayed. */
  1023.  
  1024. #pragma segment Main
  1025. //MW Changed prototype style to new C (old C to new C)
  1026. void AlertUser(short error)
  1027. {
  1028.     short        itemHit;
  1029.     Str255        message;
  1030.  
  1031.     SetCursor(&qd.arrow);
  1032.     /* type Str255 is an array in MPW 3 */
  1033.     GetIndString(message, kErrStrings, error);
  1034. //MW changed "" for "\p"
  1035.     ParamText(message, "\p", "\p", "\p");
  1036.     itemHit = Alert(rUserAlert, nil);
  1037. } /* AlertUser */
  1038.  
  1039.  
  1040. // Handle an incoming HeartBeat AppleEvent.
  1041. //
  1042. extern pascal OSErr HandleHeartBeat(AppleEvent *msg,AppleEvent *reply,long refcon)
  1043. {
  1044.  
  1045.     time_of_last_recv = TickCount();
  1046.     return (noErr);
  1047. }  // HandleHeartBeat()
  1048.  
  1049.  
  1050. // Handle and incoming Quit AppleEvent.
  1051. //
  1052. extern pascal OSErr HandleQuit(AppleEvent *msg,AppleEvent *reply,long refcon)
  1053. {
  1054.  
  1055.     TimeToQuit=TRUE;        // Time to die...
  1056.     return (noErr);
  1057. }  // HandleQuit()
  1058.  
  1059.  
  1060. // Send out version string to the server symbiont via an AppleEvent.
  1061. //
  1062. extern pascal Boolean snd_version(void)
  1063. {
  1064. char status;
  1065. char theVersion[10];
  1066. AppleEvent theAppleEvent;
  1067. AppleEvent reply;
  1068. AESendMode sendMode;
  1069. AESendPriority sendPriority;
  1070. AEAddressDesc target;
  1071. DescType returnedType;
  1072. Size returnedSize;
  1073. long timeOutInTicks;
  1074. int retval, result;
  1075.  
  1076.     BlockMove(VERSION,theVersion,5);
  1077.  
  1078.     retval=AECreateDesc(typeTargetID,&TheTarget,sizeof(TargetID),&target);
  1079.     retval=AECreateAppleEvent(kAEAUXSuite,kAEVersion,&target, kAutoGenerateReturnID,kAnyTransactionID,&theAppleEvent);
  1080.     retval=AEPutParamPtr(&theAppleEvent,keySessionID,typeInteger,&TheTarget.sessionID,sizeof(TheTarget.sessionID));
  1081.     retval=AEPutParamPtr(&theAppleEvent,keyAEJVersion,typeChar,theVersion,4);
  1082.     sendMode = kAEWaitReply;
  1083.     sendPriority = kAENormalPriority;
  1084.     timeOutInTicks = kAEDefaultTimeout;
  1085.     retval=AESend(&theAppleEvent,&reply,sendMode,sendPriority,timeOutInTicks,NULL,NULL);
  1086.     AEDisposeDesc(&target);
  1087.     AEDisposeDesc(&theAppleEvent);
  1088.     if (retval == noErr) {
  1089.         result = AEGetParamPtr(&reply,keyErrorNumber,typeChar,&returnedType,(Ptr)&status,sizeof(char),&returnedSize);
  1090.         result = AEGetParamPtr(&reply,keyErrorString,typeChar,&returnedType,(Ptr)theVersion,10,&returnedSize);
  1091.         theVersion[returnedSize]='\0';
  1092.         if (status == 'F') {
  1093.             AlertUser(eVersion);
  1094.             return FALSE;
  1095.         }
  1096.         else
  1097.             BlockMove(theVersion,&ServerOSVer,returnedSize);
  1098.     }
  1099.     else {
  1100.         AlertUser(eAESend);
  1101.         return FALSE;
  1102.     }
  1103.     return TRUE;
  1104. }  // snd_version()
  1105.  
  1106.  
  1107. // Check to see if we have received a HeartBeat AppleEvent since last check.
  1108. //
  1109. void chk_heartbeat(void)
  1110. {
  1111.     time_of_last_check = TickCount();
  1112.     if ((time_of_last_check - time_of_last_recv) > HEARTBEAT_CHECK) {
  1113.         missed_heartbeats++;
  1114.         if (missed_heartbeats >= MAX_MISSED_HEARTBEATS) {
  1115.             TimeToQuit=TRUE;
  1116.         }
  1117.     }
  1118.     else
  1119.         missed_heartbeats = 0;
  1120. }  // chk_heartheat()
  1121.  
  1122.  
  1123.  
  1124. // Send a HeartBeat AppleEvent to the server symbiont.
  1125. //
  1126. extern void snd_heartbeat(void)
  1127. {
  1128. AppleEvent theAppleEvent;
  1129. AppleEvent reply;
  1130. AESendMode sendMode;
  1131. AESendPriority sendPriority;
  1132. AEAddressDesc target;
  1133. long timeOutInTicks;
  1134. int retval;
  1135.  
  1136.     time_of_last_send=TickCount();
  1137.     retval=AECreateDesc(typeTargetID,&TheTarget,sizeof(TargetID),&target);
  1138.     retval=AECreateAppleEvent(kAEAUXSuite,kAEHeartBeat,&target, kAutoGenerateReturnID,kAnyTransactionID,&theAppleEvent);
  1139.     retval=AEPutParamPtr(&theAppleEvent,keySessionID,typeInteger,&TheTarget.sessionID,sizeof(TheTarget.sessionID));
  1140.     sendMode = kAENoReply;
  1141.     sendPriority = kAENormalPriority;
  1142.     timeOutInTicks = kAEDefaultTimeout;
  1143.     retval=AESend(&theAppleEvent,&reply,sendMode,sendPriority,timeOutInTicks,NULL,NULL);
  1144.     AEDisposeDesc(&target);
  1145.     AEDisposeDesc(&theAppleEvent);
  1146. }  // snd_heartbeat()
  1147.  
  1148.  
  1149. // Send a QUIT AppleEvent to the server symbiont.
  1150. //
  1151. extern void snd_quit(void)
  1152. {
  1153. AppleEvent theAppleEvent;
  1154. AppleEvent reply;
  1155. AESendMode sendMode;
  1156. AESendPriority sendPriority;
  1157. AEAddressDesc target;
  1158. long timeOutInTicks;
  1159. int retval;
  1160.  
  1161.     retval=AECreateDesc(typeTargetID,&TheTarget,sizeof(TargetID),&target);
  1162.     retval=AECreateAppleEvent(kAEAUXSuite,kAEQuit,&target, kAutoGenerateReturnID,kAnyTransactionID,&theAppleEvent);
  1163.     retval=AEPutParamPtr(&theAppleEvent,keySessionID,typeInteger,&TheTarget.sessionID,sizeof(TheTarget.sessionID));
  1164.     sendMode = kAENoReply;
  1165.     sendPriority = kAENormalPriority;
  1166.     timeOutInTicks = kAEDefaultTimeout;
  1167.     retval=AESend(&theAppleEvent,&reply,sendMode,sendPriority,timeOutInTicks,NULL,NULL);
  1168.     AEDisposeDesc(&target);
  1169.     AEDisposeDesc(&theAppleEvent);
  1170. }  // snd_quit()
  1171.  
  1172.  
  1173. // Send a NULL AppleEvent to the server symbiont.
  1174. // NOTE: This is usually the first event sent to a server symbiont.
  1175. //       It has the effect of actually completing the connection and
  1176. //       causes a "StartSecureSession()" to be run automatically.
  1177. //
  1178. extern void snd_null(void)
  1179. {
  1180. AppleEvent theAppleEvent;
  1181. AppleEvent reply;
  1182. AESendMode sendMode;
  1183. AESendPriority sendPriority;
  1184. AEAddressDesc target;
  1185. long timeOutInTicks;
  1186. int retval;
  1187.  
  1188.     retval=AECreateDesc(typeTargetID,&TheTarget,sizeof(TheTarget), &target);
  1189.     retval=AECreateDesc(typeNull,NULL,0, &reply);
  1190.     retval=AECreateAppleEvent(kAEAUXSuite,kAENull, &target, kAutoGenerateReturnID,kAnyTransactionID,&theAppleEvent);
  1191.     retval=AEPutParamPtr(&theAppleEvent,keySessionID,typeInteger,&TheTarget.sessionID,sizeof(TheTarget.sessionID));
  1192.     sendMode = kAENoReply;
  1193.     sendPriority = kAENormalPriority;
  1194.     timeOutInTicks = kAEDefaultTimeout;
  1195.     retval=AESend(&theAppleEvent,&reply,sendMode,sendPriority,timeOutInTicks,NULL,NULL);
  1196.     AEDisposeDesc(&target);
  1197.     AEDisposeDesc(&theAppleEvent);
  1198. }  // snd_null()
  1199.  
  1200.